//
//  MZAudioPlayer.swift
//  MZTool
//
//  Created by gby on 2023/1/30.
//

import Foundation
import AVFoundation

let mpegTimescale: Int32 = 90000

public class MZAudioPlayer: NSObject, AVAudioPlayerDelegate{
    
    private let item: AVPlayerItem
    private var audioPlayer: AVPlayer
    private var update: (TimeInterval?) -> ()
    private var timeObserver: Any?
    private var statusObserver: Any?
    
    public init?(url: URL, update: @escaping (TimeInterval?) -> ()) {
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            try AVAudioSession.sharedInstance().setActive(true)
        }catch{
            return nil
        }
        
        self.update = update
        self.item = AVPlayerItem(url: url)
        self.audioPlayer = AVPlayer(playerItem: item)
        super.init()
        
        statusObserver = self.item.observe(\.status, changeHandler: {[weak self] object , change in
            guard let s = self else {return}
            switch s.item.status {
            case .failed:
                s.removeTimer()
                s.update(nil)
            case .readyToPlay:
                s.update(s.audioPlayer.currentTime().seconds)
            case .unknown:
                s.update(0)
            default:
                break
            }
        })
        
        NotificationCenter.default.addObserver(self, selector: #selector(handleEnd(_:)), name: .AVPlayerItemDidPlayToEndTime, object: item)
        NotificationCenter.default.addObserver(self, selector: #selector(handleEnd(_:)), name: .AVPlayerItemFailedToPlayToEndTime, object: item)
    }
    
    @objc func handleEnd(_ notif: Notification){
        MZ.printLog("handleEnd")
        guard let t = notif.object as? AVPlayerItem, t == item else {
            return
        }
        removeTimer()
        setProgress(0)
        update(notif.name == .AVPlayerItemDidPlayToEndTime ? 0 : nil)
    }
    
    private func removeTimer(){
        if let to = timeObserver {
            audioPlayer.removeTimeObserver(to)
            timeObserver = nil
        }
        do {
            try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
        } catch{
            
        }
    }
    
    
    public func togglePlay() {
        if audioPlayer.rate > 0 {
            audioPlayer.pause()
            removeTimer()
        }else{
            audioPlayer.play()
            removeTimer()
            timeObserver = audioPlayer.addPeriodicTimeObserver(forInterval: CMTime(seconds: 0.02, preferredTimescale: mpegTimescale), queue: DispatchQueue.main, using: { [weak self] _ in
                guard let s = self else {return}
                s.update(s.audioPlayer.currentTime().seconds)
            })
        }
    }
    
    public func playDone() {
        if audioPlayer.rate > 0 {
            audioPlayer.pause()
            audioPlayer.seek(to: CMTime(seconds: 0, preferredTimescale: mpegTimescale))
        }
        removeTimer()
    }
    
    public func setProgress(_ time: TimeInterval){
        audioPlayer.seek(to: CMTime(seconds: time, preferredTimescale: mpegTimescale))
    }
    
    public func audioPlayerBeginInterruption(_ player: AVAudioPlayer) {
        MZ.printLog("BeginInterruption")
    }
    
    public func audioPlayerEndInterruption(_ player: AVAudioPlayer, withOptions flags: Int) {
        MZ.printLog("EndInterruption")
    }
    
    public var duration: TimeInterval{
        return item.status == .readyToPlay ? item.duration.seconds : 0
    }
    
    public var isPlaying: Bool{
        return audioPlayer.rate > 0
    }
    
    public var isPaused: Bool{
        return audioPlayer.rate == 0 && audioPlayer.currentTime().seconds > 0
    }
    
    deinit{
        removeTimer()
        MZ.printLog("")
    }
}










/**
 
 public class MZAudioPlayer: NSObject, AVAudioPlayerDelegate{
     
     private var audioPlayer: AVAudioPlayer
     private var timer: MZTimer?
     private var update: (TimeInterval?) -> ()
     
     public init?(url: URL, update: @escaping (TimeInterval?) -> ()) {
         do {
             try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
             try AVAudioSession.sharedInstance().setActive(true)
         }catch{
             return nil
         }
         
         if let player = try? AVAudioPlayer(contentsOf: url) {
             audioPlayer = player
             self.update = update
         }else{
             return nil
         }
         
         super.init()
         audioPlayer.delegate = self
     }
     
     public func togglePlay() {
         if audioPlayer.isPlaying {
             audioPlayer.pause()
             timer?.stop()
             timer = nil
         }else{
             audioPlayer.play()
             if let t = timer {
                 t.stop()
             }
             timer = MZTimer.init(handler: { [weak self] _, _ in
                 self?.update(self?.audioPlayer.currentTime)
             }, 0, 0.05)
             timer?.start()
         }
     }
     
     public func playDone() {
         if audioPlayer.isPlaying {
             audioPlayer.pause()
             audioPlayer.currentTime = 0
         }
         timer?.stop()
         timer = nil
     }
     
     public func setProgress(_ time: TimeInterval){
         audioPlayer.currentTime = time
     }
     
     public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
         timer?.stop()
         timer = nil
         update(flag ? audioPlayer.currentTime : nil)
         do {
             try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
         } catch{
             
         }
     }
     
     public func audioPlayerBeginInterruption(_ player: AVAudioPlayer) {
         MZ.printLog("BeginInterruption")
     }
     
     public func audioPlayerEndInterruption(_ player: AVAudioPlayer, withOptions flags: Int) {
         MZ.printLog("EndInterruption")
     }
     
     public var duration: TimeInterval{
         return audioPlayer.duration
     }
     
     public var isPlaying: Bool{
         return audioPlayer.isPlaying
     }
     
     public var isPaused: Bool{
         return !audioPlayer.isPlaying && audioPlayer.currentTime > 0
     }
     
     deinit{
         timer?.stop()
         timer = nil
         MZ.printLog("")
     }
 }
 
 
 */
